<template>
  <van-uploader class="upload" v-model="fileList" :multiple="multiple" :max-size="maxSize * 1024"
                :before-read="beforeRead" :after-read="afterRead" @oversize="onOversize" :max-count="maxCount"
                @delete="onDelete"></van-uploader>
</template>

<script>
import { Notify, Uploader } from 'vant'
import axios from 'axios'
import AlloyCrop from 'alloycrop'
import dayjs from 'dayjs'
import { randomString } from '@/utils'
import compress, { dataURItoBlob } from '@/utils/image'

const Request = axios.create({ timeout: 1000 * 10 })
const BEFORE_KEY = 'youth'
const TARGET = '//upload.qiniup.com'
const BASE_URL = 'https://pic.xishuw.com/'

export default {
  name: 'Upload',
  components: { [Uploader.name]: Uploader },
  props: {
    target: { type: String, default: TARGET },
    baseUrl: { type: String, default: BASE_URL },
    accept: { type: String, default: 'image/*' },
    multiple: { type: Boolean, default: false },
    maxSize: { type: Number, default: 1024 },
    maxCount: { type: Number, default: 1 },
    crop: { type: Boolean, default: false },
    cropWidth: { type: Number, default: 200 },
    cropHeight: { type: Number, default: 200 },
    cropOutput: { type: Number, default: 2 },
    fileName: { type: String, default: 'file' },
    beforeKey: { type: String, default: BEFORE_KEY },
    token: { type: String, default: '' },
    value: { type: Array, default: () => [] }
  },
  data () {
    return {
      fileList: []
    }
  },
  computed: {
    maxSizeText () {
      const { maxSize } = this
      return maxSize < 1024 ? `${maxSize}kb` : `${maxSize / 1024}m`
    }
  },
  watch: {
    value (newVal) {
      if (newVal.length && !this.init) {
        this.fileList = newVal.map(item => {
          return { url: item, status: 'done' }
        })
        this.init = true
      }
    }
  },
  mounted () {
    this.init = true
  },
  methods: {
    createKey ({ name }) {
      const { beforeKey } = this
      const date = dayjs().format('YYYY-MM-DD')
      const fileSuffix = name.substring(name.lastIndexOf('.')) || 'png'
      return `${beforeKey}/avatar/${date}/${randomString(8)}${fileSuffix}`
    },
    processFile (file) {
      if (this.crop) {
        return new Promise((resolve, reject) => {
          compress(file, { type: 'base64' }).then(({ base64 }) => {
            const { cropWidth, cropHeight, cropOutput } = this
            this.alloycrop = new AlloyCrop({
              image_src: base64,
              width: cropWidth,
              height: cropHeight,
              output: cropOutput,
              ok: (base64) => {
                const blob = dataURItoBlob(base64)
                blob.name = file.name
                blob.lastModified = file.lastModified
                blob.lastModifiedDate = file.lastModifiedDate
                resolve(blob)
              },
              cancel: () => {
                // eslint-disable-next-line prefer-promise-reject-errors
                reject()
              },
              ok_text: '确定',
              cancel_text: '取消'
            })
          }).catch(() => {
            // eslint-disable-next-line prefer-promise-reject-errors
            reject()
          })
        })
      } else {
        return compress(file)
      }
    },
    onOversize () {
      Notify(`文件大小不能超过 ${this.maxSizeText}`)
    },
    onDelete () {
      this.emitInput()
    },
    beforeRead (file) {
      const { type } = file
      return new Promise((resolve, reject) => {
        if (type.indexOf('image') !== -1) {
          this.processFile(file).then(res => {
            resolve(res)
          }).catch(() => {
            // eslint-disable-next-line prefer-promise-reject-errors
            reject()
          })
        } else {
          resolve(file)
        }
      })
    },
    afterRead (data) {
      const { token, fileName, baseUrl } = this
      const { file } = data
      const key = this.createKey(file)
      const formData = new FormData()
      formData.set('key', key)
      formData.set('token', token)
      formData.set(fileName, file)
      data.status = 'uploading'
      data.message = '上传中...'
      Request.post(this.target, formData).then(res => {
        data.status = 'done'
        data.message = ''
        data.url = baseUrl + key
        this.emitInput()
      }).catch(() => {
        data.status = 'failed'
        data.message = '上传失败'
      })
    },
    emitInput () {
      const value = this.fileList.map(item => item.url)
      this.$emit('input', value)
    }
  }
}
</script>

<style scoped lang="less">
.upload {

}
</style>
